/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author           Notes
 * 2019-07-06     heyuanjie87      the first version
 */

#include <stm32l4xx.h>
#include <rtdevice.h>
#include <rthw.h>

#ifdef BSP_USING_UART1
#define USART1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

/* Definition for USART1 Pins */
#define USART1_TX_PIN GPIO_PIN_9
#define USART1_TX_GPIO_PORT GPIOA
#define USART1_TX_AF GPIO_AF7_USART1
#define USART1_RX_PIN GPIO_PIN_10
#define USART1_RX_GPIO_PORT GPIOA
#define USART1_RX_AF GPIO_AF7_USART1

#define USART1_RX_DMA_CHANNEL DMA1_Channel5
#define USART1_RX_DMA_REUQEST DMA_REQUEST_2
#define USART1_RX_DMA_IRQN DMA1_Channel5_IRQn
#define USART1_RX_DMA_IRQHandler DMA1_Channel5_IRQHandler
#endif

#ifdef BSP_USING_UART2
#define USART2_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART2_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

/* Definition for USART2 Pins */
#define USART2_TX_PIN GPIO_PIN_2
#define USART2_TX_GPIO_PORT GPIOA
#define USART2_TX_AF GPIO_AF7_USART2
#define USART2_RX_PIN GPIO_PIN_3
#define USART2_RX_GPIO_PORT GPIOA
#define USART2_RX_AF GPIO_AF7_USART2

#define USART2_RX_DMA_CHANNEL DMA1_Channel6
#define USART2_RX_DMA_REUQEST DMA_REQUEST_2
#define USART2_RX_DMA_IRQN DMA1_Channel6_IRQn
#define USART2_RX_DMA_IRQHandler DMA1_Channel6_IRQHandler
#endif

#ifdef BSP_USING_UART3
#define USART3_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define USART3_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()

/* Definition for USART3 Pins */
#define USART3_TX_PIN GPIO_PIN_4
#define USART3_TX_GPIO_PORT GPIOC
#define USART3_TX_AF GPIO_AF7_USART3
#define USART3_RX_PIN GPIO_PIN_5
#define USART3_RX_GPIO_PORT GPIOC
#define USART3_RX_AF GPIO_AF7_USART3

#define USART3_RX_DMA_CHANNEL DMA1_Channel3
#define USART3_RX_DMA_REUQEST DMA_REQUEST_2
#define USART3_RX_DMA_IRQN DMA1_Channel3_IRQn
#define USART3_RX_DMA_IRQHandler DMA1_Channel3_IRQHandler
#endif

#ifdef BSP_USING_LPUART1
#define LPUART1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define LPUART1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()

/* Definition for LPUART1 Pins */
#define LPUART1_TX_PIN GPIO_PIN_11
#define LPUART1_TX_GPIO_PORT GPIOB
#define LPUART1_TX_AF GPIO_AF8_LPUART1
#define LPUART1_RX_PIN GPIO_PIN_10
#define LPUART1_RX_GPIO_PORT GPIOB
#define LPUART1_RX_AF GPIO_AF8_LPUART1

#define LPUART1_RX_DMA_CHANNEL DMA2_Channel7
#define LPUART1_RX_DMA_REUQEST DMA_REQUEST_4
#define LPUART1_RX_DMA_IRQN DMA2_Channel7_IRQn
#define LPUART1_RX_DMA_IRQHandler DMA2_Channel7_IRQHandler
#endif

/* STM32 uart driver */
struct stm32_uart
{
    UART_HandleTypeDef UartHandle;
    IRQn_Type irq;

    char *uart_name;
    struct rt_serial_device serial;
};

static void stm32_intrx(struct stm32_uart *uart, int en)
{
    if (en)
        __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_RXNE);
    else
        __HAL_UART_DISABLE_IT(&uart->UartHandle, UART_IT_RXNE);
}

static void stm32_inttx(struct stm32_uart *uart, int en)
{
    if (en)
        __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_TXE);
    else
        __HAL_UART_DISABLE_IT(&uart->UartHandle, UART_IT_TXE);
}

static int stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    uart->UartHandle.Init.BaudRate = cfg->baud_rate;
    uart->UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    uart->UartHandle.Init.Mode = UART_MODE_TX_RX;
    uart->UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    switch (cfg->data_bits)
    {
    case DATA_BITS_8:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
        break;
    case DATA_BITS_9:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_9B;
        break;
    default:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_1:
        uart->UartHandle.Init.StopBits = UART_STOPBITS_1;
        break;
    case STOP_BITS_2:
        uart->UartHandle.Init.StopBits = UART_STOPBITS_2;
        break;
    default:
        uart->UartHandle.Init.StopBits = UART_STOPBITS_1;
        break;
    }

    switch (cfg->parity)
    {
    case PARITY_NONE:
        uart->UartHandle.Init.Parity = UART_PARITY_NONE;
        break;
    case PARITY_ODD:
        uart->UartHandle.Init.Parity = UART_PARITY_ODD;
        break;
    case PARITY_EVEN:
        uart->UartHandle.Init.Parity = UART_PARITY_EVEN;
        break;
    default:
        uart->UartHandle.Init.Parity = UART_PARITY_NONE;
        break;
    }

    if (HAL_UART_Init(&uart->UartHandle) != HAL_OK)
    {
        return -RT_ERROR;
    }

    if (uart->UartHandle.Instance == LPUART1)
    {
        UART_WakeUpTypeDef WakeUpSelection;
        WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;

        if (HAL_UARTEx_StopModeWakeUpSourceConfig(&uart->UartHandle, WakeUpSelection) != HAL_OK)
        {
            return -1;
        }
        HAL_UARTEx_EnableStopMode(&uart->UartHandle);
    }

    return 0;
}

static int stm32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    int ret = 0;
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    switch (cmd)
    {
    default:
    {
        ret = -1;
    }
    break;
    }

    return ret;
}

static int stm32_putc(struct rt_serial_device *serial, char c)
{
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    if (!__HAL_UART_GET_FLAG(&(uart->UartHandle), UART_FLAG_TXE))
        return 0;

    uart->UartHandle.Instance->TDR = c;

    return 1;
}

static int stm32_getc(struct rt_serial_device *serial)
{
    int ch = -1;
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_RXNE) != RESET)
        ch = uart->UartHandle.Instance->RDR & 0xff;

    return ch;
}

static int stm32_init(struct rt_serial_device *serial)
{
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    HAL_NVIC_SetPriority(uart->irq, 1, 0);
    HAL_NVIC_EnableIRQ(uart->irq);

    return 0;
}

static void stm32_deinit(struct rt_serial_device *serial)
{
}

/**
 * Uart common interrupt process. This need add to uart ISR.
 *
 * @param serial serial device
 */
static void uart_isr(struct rt_serial_device *serial)
{
    struct stm32_uart *uart;

    /* enter interrupt */
    rt_interrupt_enter();

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    /* UART in mode Receiver -------------------------------------------------*/
    if ((__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_RXNE) != RESET) &&
        (__HAL_UART_GET_IT_SOURCE(&uart->UartHandle, UART_IT_RXNE) != RESET))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        /* Clear RXNE interrupt flag */
        __HAL_UART_CLEAR_FLAG(&uart->UartHandle, UART_FLAG_RXNE);
    }
    else
    {
        if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_ORE) != RESET)
        {
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_OVERRUN);
            __HAL_UART_CLEAR_OREFLAG(&uart->UartHandle);
        }
        if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_NE) != RESET)
        {
            __HAL_UART_CLEAR_NEFLAG(&uart->UartHandle);
        }
        if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_FE) != RESET)
        {
            __HAL_UART_CLEAR_FEFLAG(&uart->UartHandle);
        }
        if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_PE) != RESET)
        {
            __HAL_UART_CLEAR_PEFLAG(&uart->UartHandle);
        }
        if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_WUF) != RESET)
        {
            __HAL_UART_CLEAR_FLAG(&uart->UartHandle, UART_CLEAR_WUF);
        }
        if (__HAL_UART_GET_IT(&uart->UartHandle, UART_IT_TXE))
        {
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);
        }
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

static int stm32_xmitctl(struct rt_serial_device *serial, int cmd, int arg)
{
    int ret = 0;
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;

    switch (cmd)
    {
    case UART_XMIT_RX:
    {
        stm32_intrx(uart, arg);
    }
    break;
    case UART_XMIT_TX:
    {
        stm32_inttx(uart, arg);
    }
    break;
    default:
    {
        ret = -1;
    }
    break;
    }

    return ret;
}

static const struct rt_uart_ops _uart_ops = {
    stm32_configure,
    stm32_control,
    stm32_putc,
    stm32_getc,
    stm32_init,
    stm32_deinit,
    stm32_xmitctl};

#if defined(BSP_USING_UART1)
/* UART1 device driver structure */
static struct stm32_uart uart1 = {
    {USART1},    // UART_HandleTypeDef UartHandle;
    USART1_IRQn, // IRQn_Type irq;
    "uart1",     // char * uart_name;
};

void USART1_IRQHandler(void)
{
    uart_isr(&uart1.serial);
}
#endif /* BSP_USING_UART1 */

#if defined(BSP_USING_UART2)
/* UART2 device driver structure */
static struct stm32_uart uart2 = {
    {USART2},    // UART_HandleTypeDef UartHandle;
    USART2_IRQn, // IRQn_Type irq;
    "uart2",     // char * uart_name;
};

void USART2_IRQHandler(void)
{
    uart_isr(&uart2.serial);
}
#endif /* BSP_USING_UART2 */

#if defined(BSP_USING_UART3)
/* UART3 device driver structure */
static struct stm32_uart uart3 = {
    {USART3},    // UART_HandleTypeDef UartHandle;
    USART3_IRQn, // IRQn_Type irq;
    "uart3",     // char * uart_name;
};

void USART3_IRQHandler(void)
{
    uart_isr(&uart3.serial);
}
#endif /* BSP_USING_UART3 */

#if defined(BSP_USING_LPUART1)
/* lpuart1 device driver structure */
static struct stm32_uart lpuart1 = {
    {LPUART1},    // UART_HandleTypeDef UartHandle;
    LPUART1_IRQn, // IRQn_Type irq;
    "lpuart1",    // char * uart_name;
};

void LPUART1_IRQHandler(void)
{
    uart_isr(&lpuart1.serial);
}
#endif /* BSP_USING_LPUART1 */

void rt_hw_console_output(char c)
{
    UART_HandleTypeDef *uh;

    uh = &uart1.UartHandle;
    uh->Instance->TDR = c;
    while ((__HAL_UART_GET_FLAG(uh, UART_FLAG_TC) == RESET))
        ;
}

int rt_hw_console_init(void)
{
    UART_HandleTypeDef *uh;

    uh = &uart1.UartHandle;

    uh->Init.BaudRate = 115200;
    uh->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    uh->Init.Mode = UART_MODE_TX_RX;
    uh->Init.OverSampling = UART_OVERSAMPLING_16;
    uh->Init.WordLength = UART_WORDLENGTH_8B;
    uh->Init.StopBits = UART_STOPBITS_1;
    uh->Init.Parity = UART_PARITY_NONE;

    return HAL_UART_Init(uh);
}
INIT_BOARD_EXPORT(rt_hw_console_init);

int stm32_hw_usart_init(void)
{
    int i;
    struct stm32_uart *uarts[] = {
#ifdef BSP_USING_UART1
        &uart1,
#endif

#ifdef BSP_USING_UART2
        &uart2,
#endif

#ifdef BSP_USING_UART3
        &uart3,
#endif

#ifdef BSP_USING_LPUART1
        &lpuart1,
#endif
    };

    for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++)
    {
        struct stm32_uart *uart = uarts[i];
        int result;

        uart->serial.ops = &_uart_ops;

        /* register UART device */
        result = rt_hw_serial_register(&uart->serial,
                                       uart->uart_name,
                                       0,
                                       uart);

        RT_ASSERT(result == 0);
        (void)result;
    }

    return 0;
}
INIT_DEVICE_EXPORT(stm32_hw_usart_init);
